#include <stdio.h>
#include <string.h>
#include <imbus_internal.h>
#include <im_hash.h>
#include <im_array.h>
#include "im_x11_keysyms.h"
#include "im_x11_keysym_unicode.h"

int compare_key_unicode_pair_by_sym (IMConstPointer v1, IMConstPointer v2)
{
    return ((int)((const KeyUnicodePair*)v1)->sym) - ((int)((const KeyUnicodePair*)v2)->sym);
}

IMUInt32 keysym_to_unicode (IMUInt32 sym)
{
    KeyUnicodePair u;
    KeyUnicodePair *p;

    u.sym = sym;
    u.uni = 0;

    p = im_lower_bound (key_to_unicode, NUM_KEY_UNICODES, sizeof (KeyUnicodePair), &u, compare_key_unicode_pair_by_sym);

    if (p && p->sym == sym)
        return p->uni;

    return 0;
}

IMBool foreach_apply_unicode (IMPointer key, IMPointer val, IMPointer user_data)
{
    IMUInt32  sym = IM_POINTER_TO_UINT(key);
    Keysym   *p   = (Keysym *) val;

    if (!p->uni)
        p->uni = keysym_to_unicode (sym);
    return TRUE;
}

IMBool foreach_insert_to_array (IMPointer key, IMPointer val, IMPointer user_data)
{
    im_ptr_array_append ((IMPtrArray*)user_data, val);
    return TRUE;
}

int compare_keysym_by_sym_inv (IMConstPointer v1, IMConstPointer v2)
{
    return ((int) ((Keysym*)v2)->sym) - ((int) ((Keysym*)v1)->sym);
}

int compare_keysym_by_sym (IMConstPointer v1, IMConstPointer v2)
{
    return ((int) ((Keysym*)v1)->sym) - ((int) ((Keysym*)v2)->sym);
}

int compare_keysym_by_uni (IMConstPointer v1, IMConstPointer v2)
{
    return ((int) ((Keysym*)v1)->uni) - ((int) ((Keysym*)v2)->uni);
}

int compare_keysym_by_name (IMConstPointer v1, IMConstPointer v2)
{
    return strcmp (((Keysym*)v1)->name, ((Keysym*)v2)->name);
}

void foreach_apply_special_sym (IMPointer v, IMPointer u)
{
    Keysym   *key = (Keysym*)v;

    if (key->uni == 0) {
        if (key->sym <= 0xffff)
            key->uni = key->sym + 0x110000;
        else
            fprintf (stderr, "Special keysym 0x%x is larger than 0xffff.\n");
    }
}

void foreach_print_keycode_def_special (IMPointer v, IMPointer u)
{
    Keysym *key = (Keysym*) v;
    FILE *fp = (FILE*) u;

    if (key->uni >= 0x110000)
        fprintf (fp, "    IM_KEY_%-28s = 0x%04x,    /** X11 keysym = 0x%04x:   **/\n", 
             key->name, key->uni, key->sym);
}

void foreach_print_keycode_def_uni (IMPointer v, IMPointer u)
{
    Keysym *key = (Keysym*) v;
    FILE *fp = (FILE*) u;
    IMChar buf [6];

    if (key->uni < 0x110000) {
        if (key->uni >= 0x20)
            buf [im_unichar_to_utf8 (buf, key->uni, 6)] = 0;
        else {
            buf [0] = ' ';
            buf [1] = 0;
        }

        fprintf (fp, "    IM_KEY_%-28s = 0x%04x,    /** X11 keysym = 0x%04x: %s **/\n", 
             key->name, key->uni, key->sym, buf);
    }
}

void foreach_print_uni_sym (IMPointer v, IMPointer u)
{
    Keysym *key = (Keysym*) v;
    FILE *fp = (FILE*) u;

    fprintf (fp, "  { 0x%04x, 0x%04x },   /** %s **/\n", key->uni, key->sym, key->name);
}

void foreach_print_name_uni (IMPointer v, IMPointer u)
{
    Keysym *key = (Keysym*) v;
    FILE *fp = (FILE*) u;
    IMChar buf [64];

    snprintf (buf, 64, "\"%s\"", key->name);

    fprintf (fp, "  { %-30s, 0x%04x },   /** X11 keysym = 0x%04x **/\n", buf, key->uni, key->sym);
}

int main (int argc, char *argv [])
{
    IMPtrArray  *array;
    IMHashTable *table;
    IMSize i;
    IMUInt32 special = 0xd800;
    FILE *fp;

    im_init (&argc, &argv);

    array = im_ptr_array_new ();
    table = im_hash_table_new (im_direct_hash, im_direct_equal);

    for (i = 0; i < NUM_KEYSYMS; ++i) {
        Keysym *p = im_hash_table_lookup (table, IM_UINT_TO_POINTER(keysyms[i].sym));
        if (p) {
            const IMChar *old = p->name;
            const IMChar *new = keysyms[i].name;
            if (strcmp (old,new) != 0)
                printf ("Duplicate: 0x%07x U+%04x  %-24s -> %s\n", p->sym, p->uni, old, new);
            continue;
        }
        im_hash_table_insert (table, IM_UINT_TO_POINTER (keysyms [i].sym), &keysyms[i]);
    }

    im_hash_table_foreach (table, foreach_apply_unicode, 0);

    im_hash_table_foreach (table, foreach_insert_to_array, array);

    im_ptr_array_sort (array, compare_keysym_by_sym);
    im_ptr_array_foreach (array, foreach_apply_special_sym, 0);
    im_ptr_array_sort (array, compare_keysym_by_uni);

    fp = fopen ("im_key_codes.h", "wt");

    if (!fp) {
        fprintf (stderr, "Can't open im_key_defines.h\n");
        return -1;
    }

    fprintf (fp, "    /* Special key codes */\n");
    im_ptr_array_foreach (array, foreach_print_keycode_def_special, fp);
    fprintf (fp, "\n    /* Unicode key codes */\n");
    im_ptr_array_foreach (array, foreach_print_keycode_def_uni, fp);
    fclose (fp);

    fp = fopen ("im_key_mappings.h", "wt");
    if (!fp) {
        fprintf (stderr, "Can't open im_key_data.h\n");
        return -1;
    }

    fprintf (fp,
"typedef struct\n"
"{\n"
"    IMUInt32 key_code;\n"
"    IMUInt32 x11_keysym;\n"
"}IMKeyCodeX11keysymPair;\n"
"typedef struct\n"
"{\n"
"    const IMChar *name;\n"
"    IMUInt32      key_code;\n"
"}IMKeyNameCodePair;\n\n"
"static IMKeyCodeX11keysymPair im_key_code_to_x11keysym_map [] =\n"
"{\n");
    im_ptr_array_foreach (array, foreach_print_uni_sym, fp);
    im_ptr_array_sort (array, compare_keysym_by_sym);

    fprintf (fp,
"\n};\n\n"
"static IMKeyCodeX11keysymPair im_x11keysym_to_key_code_map [] =\n"
"{\n");
    im_ptr_array_foreach (array, foreach_print_uni_sym, fp);

    fprintf (fp,
"\n};\n\n"
"static IMKeyNameCodePair im_key_name_to_code_map [] =\n"
"{\n");
    im_ptr_array_sort (array, compare_keysym_by_name);
    im_ptr_array_foreach (array, foreach_print_name_uni, fp);

    fprintf (fp,
"\n};\n\n"
"static IMKeyNameCodePair im_key_code_to_name_map [] =\n"
"{\n");
    im_ptr_array_sort (array, compare_keysym_by_uni);
    im_ptr_array_foreach (array, foreach_print_name_uni, fp);
    fprintf (fp,
"\n};\n\n");
    fclose (fp);

    im_ptr_array_destroy (array, TRUE);
    im_hash_table_destroy (table);

    return 0;
}
/*
vi:ts=4:nowrap:ai:expandtab
*/
